package com.jaws.core.common.shard.util;

import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.dialect.mysql.parser.MySqlStatementParser;
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlSchemaStatVisitor;
import com.alibaba.druid.stat.TableStat;
import com.alibaba.druid.stat.TableStat.Condition;
import com.alibaba.druid.stat.TableStat.Name;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.jaws.core.common.log.Log;
import com.jaws.core.common.shard.domain.MysqlOperationEnum;
import com.jaws.core.common.shard.domain.TableParse;
import lombok.extern.slf4j.Slf4j;

import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * druid 解析 sql
 *
 * @author liangqm
 * @date 2018-05-04 18:29:18
 * @since v1.0.2
 */
@Slf4j
public class DruidParse {


	/**
	 * 替换sql表名
	 *
	 * @param statement     ''
	 * @param originalTable 原表名
	 * @param newTable      新表名
	 * @return String
	 * @author liangqm
	 * @date 2018-05-04 19:05:18
	 * @since v1.0.2
	 */
	public static String replaceTable(SQLStatement statement, String originalTable, String newTable) {
		TableNameModifier tnm = new TableNameModifier(originalTable, newTable);
		statement.accept(tnm);
		return statement.toString();
	}


	/**
	 * @param sql mybatis mapper sql
	 * @return List<SQLStatement>
	 * @author liangqm
	 * @date 2018-05-09 11:27:19
	 * @since v1.0.2
	 */
	public static List<SQLStatement> obtainStatementList(String sql) {
		MySqlStatementParser parser = new MySqlStatementParser(sql);
		List<SQLStatement> statementList = parser.parseStatementList();
		return statementList;
	}

	public static Map<String, List<Object>> parseSqlCondition(String sql) {
		Map<String, List<Object>> conditionMap = Maps.newHashMap();

		TableParse tableParse = new TableParse();
		MySqlStatementParser parser = new MySqlStatementParser(sql);
		List<SQLStatement> statementList = parser.parseStatementList();
		tableParse.setStatementList(statementList);
		for (SQLStatement statement : statementList) {
			MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor();
			statement.accept(visitor);
			List<Condition> conditions = visitor.getConditions();
			for (Condition condition : conditions) {
				conditionMap.put(condition.getColumn().toString(), condition.getValues());
			}
		}
		return conditionMap;
	}


	/**
	 * 解析sql
	 *
	 * @param sql ''
	 * @return TableParse
	 * @author liangqm
	 * @date 2018-05-04 18:30:18
	 * @since v1.0.2
	 */
	public static TableParse parseSql(String sql) {

		TableParse tableParse = new TableParse();

		MySqlStatementParser parser = new MySqlStatementParser(sql);
		List<SQLStatement> statementList = parser.parseStatementList();
		tableParse.setStatementList(statementList);
		log.info(Log.op("DruidParse.parseSql").kv("statementList", statementList).toString());

		for (SQLStatement statement : statementList) {
			// 初始化 visitor
			MySqlSchemaStatVisitor visitor = new MySqlSchemaStatVisitor();
			statement.accept(visitor);

			// 获取操作名称和表名称
			obtainTablesAndOperation(tableParse, visitor);

			// 获取操作列名
			obtainFilds(tableParse, visitor);

			// 获取where 条件 键值对
			obtainConditionMap(tableParse, visitor);


		}
		return tableParse;
	}


	/**
	 * 获取where 条件键值对
	 *
	 * @param tableParse ''
	 * @param visitor    ''
	 * @author liangqm
	 * @date 2018-05-04 18:51:18
	 * @since v1.0.2
	 */
	private static void obtainConditionMap(TableParse tableParse, MySqlSchemaStatVisitor visitor) {
		Map<String, List<Object>> conditionMap = Maps.newHashMap();
		List<Condition> conditions = visitor.getConditions();
		log.info(Log.op("DruidParse.parseSql").kv("conditions", conditions).toString());
		for (Condition condition : conditions) {
			conditionMap.put(condition.getColumn().toString(), condition.getValues());
		}
		tableParse.setConditionsMap(conditionMap);
	}

	/**
	 * 获取操作列名称
	 *
	 * @param tableParse ''
	 * @param visitor    ''
	 * @author liangqm
	 * @date 2018-05-04 18:51:18
	 * @since v1.0.2
	 */
	private static void obtainFilds(TableParse tableParse, MySqlSchemaStatVisitor visitor) {
		List<String> columnList = Lists.newArrayList();
		for (TableStat.Column column : visitor.getColumns()) {
			columnList.add(column.toString());
		}
		tableParse.setFields(columnList);

	}

	/**
	 * 获取sql 中所有表名&sql操作类型
	 *
	 * @param tableParse ''
	 * @param visitor    ''
	 * @author liangqm
	 * @date 2018-05-04 18:45:18
	 * @since v1.0.2
	 */
	private static void obtainTablesAndOperation(TableParse tableParse, MySqlSchemaStatVisitor visitor) {
		log.info(Log.op("DruidParse.obtainTablesAndOperation").kv("tables", visitor.getTables()).toString());
		Map<Name, TableStat> tableMap = visitor.getTables();
		Iterator<Map.Entry<Name, TableStat>> tableEntryIterator = tableMap.entrySet().iterator();
		List<String> tables = Lists.newArrayList();
		MysqlOperationEnum tableOperationEnum = null;
		while (tableEntryIterator.hasNext()) {
			Map.Entry<Name, TableStat> entry = tableEntryIterator.next();
			tables.add(entry.getKey().getName());
			tableOperationEnum = MysqlOperationEnum.valueOf(entry.getValue().toString());
		}
		tableParse.setOperationType(tableOperationEnum);
		tableParse.setTables(tables);
	}


	public static void main(String[] args) {
//		String sql = "select IFNULL(SUM(amount),0) from S65.repay_batch_order "
//				+ " left join S65.repay_order_detail on repay_order_detail.repay_batch_id=repay_batch_order.order_id and repay_order_detail.state=0"
//				+ " left join S62.T6251 ON T6251.F01=repay_order_detail.credit_id "
//				+ " where  T6251.F13<> 'YY'  "
//				+ " and repay_batch_order.state=0 "
//				+ " and repay_order_detail.transaction_code='7002' "
//				+ " and repay_order_detail.in_u_id=2045";

		String sql = "update account_trade set user_id = 10000 where account_no =123";

//		String sql = "select * from account_trade ,account_info  where account_trade.user_id = ? and account_trade.account_trade= ? and account_trade.trade_type = '01' and account_trade.account_no = account_info.account_no";

//		String sql = "insert into account_trade (trade_no, out_trade_no) values (100,200)";
		TableParse tableParse = parseSql(sql);
		System.out.println(JSON.toJSONString(tableParse));
	}

}
